#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
片段管理模块
负责管理视频片段的标记、存储和处理
"""

from typing import List, Tuple, Optional
from dataclasses import dataclass
from enum import Enum


class SegmentType(Enum):
    """片段类型枚举"""
    KEEP = 1    # 保留片段
    DELETE = 2  # 删除片段


@dataclass
class VideoSegment:
    """视频片段数据类"""
    start_time: float      # 开始时间（秒）
    end_time: float        # 结束时间（秒）
    segment_type: SegmentType  # 片段类型
    description: str = ""  # 描述信息
    
    def duration(self) -> float:
        """获取片段持续时间"""
        return self.end_time - self.start_time
    
    def __str__(self) -> str:
        """字符串表示"""
        type_str = "保留" if self.segment_type == SegmentType.KEEP else "删除"
        return f"[{type_str}] {self.start_time:.2f}s - {self.end_time:.2f}s ({self.duration():.2f}s)"


class SegmentManager:
    """
    片段管理器类
    
    负责管理视频片段的标记、存储和处理。支持两种模式：
    1. 保留模式：标记需要保留的视频片段
    2. 删除模式：标记需要删除的视频片段
    
    片段管理器维护一个片段列表，确保片段之间没有时间重叠，
    并提供片段的增删改查功能。
    """
    
    def __init__(self):
        """
        初始化片段管理器
        
        创建空的片段列表，设置默认标记模式为保留模式，
        并初始化临时起始时间点为None。
        """
        self.segments: List[VideoSegment] = []
        self.current_mode: SegmentType = SegmentType.KEEP  # 默认为保留模式
        self.temp_start_time: Optional[float] = None       # 临时起始时间点
        
    def set_mode(self, mode: SegmentType) -> bool:
        """
        设置标记模式
        
        根据传入的片段类型设置当前的标记模式。标记模式决定了
        新创建的片段是保留片段还是删除片段。
        
        Args:
            mode (SegmentType): 片段类型（保留或删除）
                - SegmentType.KEEP: 保留模式
                - SegmentType.DELETE: 删除模式
            
        Returns:
            bool: 设置成功返回True
            
        Example:
            >>> manager = SegmentManager()
            >>> manager.set_mode(SegmentType.DELETE)
            True
        """
        self.current_mode = mode
        return True
        
    def mark_start(self, time: float) -> bool:
        """
        标记片段起始点（入点）
        
        设置片段的起始时间点，该时间点会被临时存储，直到调用mark_end方法
        完成片段的创建。此方法会验证时间点的有效性。
        
        Args:
            time (float): 起始时间点（秒），必须为非负数
            
        Returns:
            bool: 标记成功返回True，时间点无效则返回False
            
        Note:
            - 时间点必须大于等于0
            - 此方法不会创建完整的片段，需要配合mark_end使用
            - 如果之前已设置起始点但未完成片段创建，新值会覆盖旧值
            
        Example:
            >>> manager = SegmentManager()
            >>> manager.mark_start(10.5)  # 标记10.5秒为起始点
            True
        """
        # 验证时间点是否有效
        if time < 0:
            print("错误：时间点不能为负数")
            return False
            
        # 设置起始点
        self.temp_start_time = time
        return True
        
    def mark_end(self, time: float) -> bool:
        """
        标记片段结束点（出点）并创建完整片段
        
        完成片段的创建过程，需要与之前调用的mark_start方法配合使用。
        此方法会验证时间点的有效性、检查片段是否与现有片段重叠，
        并最终将新片段添加到片段列表中。
        
        Args:
            time (float): 结束时间点（秒），必须为非负数
            
        Returns:
            bool: 标记成功返回True，时间点无效或片段重叠则返回False
            
        Note:
            - 时间点必须大于等于0
            - 必须先调用mark_start设置起始点
            - 结束时间必须大于起始时间
            - 新片段不能与现有片段在时间上重叠
            - 成功创建片段后会自动清除临时起始点
            
        Example:
            >>> manager = SegmentManager()
            >>> manager.mark_start(10.0)  # 标记起始点
            True
            >>> manager.mark_end(20.0)    # 标记结束点并创建片段
            True
        """
        # 验证时间点是否有效
        if time < 0:
            print("错误：时间点不能为负数")
            return False
            
        if self.temp_start_time is None:
            print("错误：请先标记入点")
            return False
            
        if time <= self.temp_start_time:
            print("错误：出点时间必须大于入点时间")
            return False
            
        # 创建片段
        segment = VideoSegment(
            start_time=self.temp_start_time,
            end_time=time,
            segment_type=self.current_mode
        )
        
        # 验证片段是否与现有片段重叠
        if self._is_overlapping(segment):
            print("错误：片段与现有片段重叠")
            return False
            
        # 添加片段到列表
        self.segments.append(segment)
        
        # 清除临时起始点
        self.temp_start_time = None
        
        return True
        
    def get_segments(self) -> List[VideoSegment]:
        """
        获取所有片段的副本
        
        返回当前管理的所有视频片段的副本列表，按创建顺序排列。
        返回副本是为了防止外部代码意外修改内部片段列表。
        
        Returns:
            List[VideoSegment]: 片段列表的副本，按创建顺序排列
            
        Note:
            - 返回的是副本，修改返回列表不会影响内部数据
            - 片段按创建顺序排列，不是按时间顺序
            - 如需按时间顺序排列的片段，请使用get_final_segments方法
            
        Example:
            >>> manager = SegmentManager()
            >>> segments = manager.get_segments()
            >>> print(f"共有{len(segments)}个片段")
        """
        return self.segments[:]
        
    def get_segments_by_type(self, segment_type: SegmentType) -> List[VideoSegment]:
        """
        根据类型获取片段
        
        筛选出指定类型的片段并返回新的列表。常用于分离保留片段和删除片段。
        
        Args:
            segment_type (SegmentType): 片段类型
                - SegmentType.KEEP: 保留片段
                - SegmentType.DELETE: 删除片段
            
        Returns:
            List[VideoSegment]: 指定类型的片段列表，按创建顺序排列
            
        Note:
            - 返回的是新列表，不与内部数据共享内存
            - 列表按创建顺序排列，不是按时间顺序
            
        Example:
            >>> manager = SegmentManager()
            >>> # 获取所有保留片段
            >>> keep_segments = manager.get_segments_by_type(SegmentType.KEEP)
            >>> print(f"共有{len(keep_segments)}个保留片段")
        """
        return [segment for segment in self.segments if segment.segment_type == segment_type]
        
    def remove_segment(self, index: int) -> bool:
        """
        删除指定索引的片段

        根据索引位置删除片段列表中的指定片段。索引从0开始计数。
        删除成功后会打印被删除片段的信息。
        
        Args:
            index (int): 片段索引，必须在有效范围内（0 <= index < 片段数量）
            
        Returns:
            bool: 删除成功返回True，索引无效或删除过程中出错则返回False
            
        Note:
            - 索引从0开始
            - 索引必须小于片段列表的长度
            - 删除操作是不可逆的
            - 成功删除后会自动打印被删除片段的详细信息
            
        Example:
            >>> manager = SegmentManager()
            >>> # 假设已添加了一些片段
            >>> manager.remove_segment(0)  # 删除第一个片段
            已删除片段: [保留] 10.00s - 20.00s (10.00s)
            True
        """
        if 0 <= index < len(self.segments):
            try:
                removed_segment = self.segments.pop(index)
                print(f"已删除片段: {removed_segment}")
                return True
            except Exception as e:
                print(f"删除片段时出错: {e}")
                return False
        else:
            print(f"错误：无效的片段索引 {index}")
            return False
            
    def clear_segments(self) -> None:
        """
        清空所有片段
        
        删除所有已标记的片段，并重置临时起始时间点。
        此操作不可逆，执行前建议进行确认。
        
        Note:
            - 此操作会删除所有片段（保留和删除类型）
            - 会重置临时起始时间点为None
            - 操作不可逆，执行前建议进行用户确认
            - 成功执行后会打印确认信息
            
        Example:
            >>> manager = SegmentManager()
            >>> # 假设已添加了一些片段
            >>> manager.clear_segments()
            已清空所有片段
        """
        self.segments.clear()
        self.temp_start_time = None
        print("已清空所有片段")
        
    def get_segment_at_time(self, time: float) -> Optional[VideoSegment]:
        """
        获取指定时间点所在的片段
        
        遍历所有片段，查找包含指定时间点的片段。如果找到则返回该片段，
        否则返回None。主要用于检查当前播放时间是否在某个已标记片段内。
        
        Args:
            time (float): 时间点（秒），通常为视频播放的当前位置
            
        Returns:
            VideoSegment: 包含指定时间点的片段对象，如果不在任何片段中则返回None
            
        Note:
            - 时间点必须大于等于0
            - 如果多个片段包含同一时间点（理论上不应该发生），返回第一个匹配的片段
            - 返回的片段是原始对象的引用，不是副本
            
        Example:
            >>> manager = SegmentManager()
            >>> # 假设已添加了一个从10.0到20.0秒的片段
            >>> segment = manager.get_segment_at_time(15.0)
            >>> print(segment)
            [保留] 10.00s - 20.00s (10.00s)
        """
        for segment in self.segments:
            if segment.start_time <= time <= segment.end_time:
                return segment
        return None
        
    def validate_segments(self) -> bool:
        """
        验证片段有效性
        
        检查所有片段是否存在时间重叠的情况。按照起始时间排序后，
        检查相邻片段的时间是否有重叠。如果没有重叠则认为所有片段有效。
        
        Returns:
            bool: 所有片段有效且无时间重叠返回True，存在重叠则返回False
            
        Note:
            - 此方法不影响片段列表的实际顺序
            - 仅用于验证，不修改任何数据
            - 如果发现重叠会打印警告信息
            - 片段管理器在添加片段时已经做了重叠检查，此方法作为额外验证
            
        Example:
            >>> manager = SegmentManager()
            >>> # 添加一些片段后
            >>> is_valid = manager.validate_segments()
            >>> if is_valid:
            ...     print("所有片段均有效")
            ... else:
            ...     print("存在时间重叠的片段")
        """
        # 检查时间重叠
        sorted_segments = sorted(self.segments, key=lambda s: s.start_time)
        
        for i in range(len(sorted_segments) - 1):
            if sorted_segments[i].end_time > sorted_segments[i + 1].start_time:
                print(f"警告：片段时间重叠 {sorted_segments[i]} 和 {sorted_segments[i + 1]}")
                return False
                
        return True
        
    def get_final_segments(self) -> List[VideoSegment]:
        """
        获取最终用于拼接的片段（保留模式下的片段）
        
        从所有标记的片段中筛选出保留模式的片段，并按起始时间升序排列。
        这些片段将用于视频导出器的拼接操作，生成最终的视频文件。
        
        Returns:
            List[VideoSegment]: 保留的片段列表，按起始时间升序排列
            
        Note:
            - 只返回标记为保留（KEEP）类型的片段
            - 返回的片段列表按起始时间排序，确保拼接顺序正确
            - 返回的是新列表，不与内部数据共享内存
            - 如果没有保留片段，返回空列表
            
        Example:
            >>> manager = SegmentManager()
            >>> # 添加一些保留和删除片段
            >>> final_segments = manager.get_final_segments()
            >>> print(f"共有{len(final_segments)}个片段用于拼接")
        """
        keep_segments = self.get_segments_by_type(SegmentType.KEEP)
        return sorted(keep_segments, key=lambda s: s.start_time)


if __name__ == "__main__":
    # 测试代码
    manager = SegmentManager()
    
    # 测试标记功能
    manager.mark_start(10.0)
    manager.mark_end(20.0)
    
    manager.set_mode(SegmentType.DELETE)
    manager.mark_start(30.0)
    manager.mark_end(40.0)
    
    print("所有片段:")
    for segment in manager.get_segments():
        print(segment)